<?php
namespace Api;

use Api\Application\Benchmark;
use Api\Application\Bootstrap;
use Api\Application\Error;
use Api\Autoloader\Exception\ClassNotFoundException;
use Api\Http\Response;
use Api\Utils\ArrayObject;
use Api\Utils\Arrays;

/**
 * Class Application
 * @package Api
 * @author Steffen Brem <steffenbrem@gmail.com>
 */
class Application
{
    const PRODUCTION        = 'production';
    const DEVELOPMENT       = 'development';

    const MODE_STANDALONE   = 'standalone';
    const MODE_DEPENDENT    = 'dependent';

    /**
     * @var Application
     */
    static public $shared;

    /**
     * @var Utils\ArrayObject|array
     */
    public $config = array();

    /**
     * @var Autoloader
     */
    public $autoloader;

    /**
     * @var Bootstrap
     */
    public $bootstrap;

    /**
     * @param $namespace
     * @param $path
     * @param array $config
     */
    public function __construct($namespace, $path, array $config = array())
    {
        // Start output buffering
        ob_start();

        require_once __DIR__ . "/utils/ArrayObject.php";
        require_once __DIR__ . "/utils/Arrays.php";

        // Default configuration
        $defaultConfig = array(
            'application' => array(
                'namespace'     => $namespace,
                'path'          => realpath($path),
                'environment'   => Application::DEVELOPMENT,
                'debug'         => true,
                'mode'          => self::MODE_STANDALONE
            )
        );

        if (!file_exists($path))
        {
            die("Application path '" . $path . "' does not exist.");
        }

        $settings = $this->_getSettings($defaultConfig['application']['path'] . '/config/settings.ini');
        $defaultConfig = Arrays::merge($defaultConfig, Arrays::merge($settings, $config));

        $this->config = new ArrayObject($defaultConfig);
    }

    /**
     * Run application
     */
    public function run()
    {
        self::$shared = $this;

        require_once __DIR__ . "/Autoloader.php";

        $this->autoloader = new Autoloader();

        // Register namespaces
        $this->autoloader->register('Api', dirname(__FILE__));
        $this->autoloader->register($this->config['application']['namespace'], $this->config['application']['path']);

        // Check if the application has bootstrap file
        $class = 'Api\Application\Bootstrap';

        $path = $this->config['application']['path'] . '/Bootstrap.php';

        if (file_exists($path))
        {
            $class = $this->config['application']['namespace'] . '\Bootstrap';

            if (!class_exists($class))
            {
                throw new ClassNotFoundException("Bootstrap class '" . $class . "' could not be loaded, malformed class name. Check your bootstrap file: " . $path);
            }
        }

        // Create application bootstrap
        $this->bootstrap = new $class;

        // Setup error handling before dispatching the application
        $this->_setupErrorHandling();

        try
        {
            // Dispatch application
            $r = $this->bootstrap->dispatch();

            if ($r instanceof Response)
            {
                $r->output();
            }
        }
        catch (\Exception $e)
        {
            $error = new Error();
            $error->displayException($e);
        }
    }

    /**
     * Process settings.ini file
     */
    private function _getSettings($file)
    {
        $settings = array();

        if (file_exists($file))
        {
            $settings = parse_ini_file($file, true);
        }

        return $settings;
    }

    /**
     * Prepare application
     */
    private function _setupErrorHandling()
    {
        // When a normal php error occurred, we throw an ErrorException
        set_error_handler(function($errno, $errstr, $errfile, $errline) {
            throw new \ErrorException($errstr, $errno, 1, $errfile, $errline);
        });
    }
}